package org.tri.persistence.search;

import java.util.List;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.util.HibernateUtil;

/**
 * La clase {@code Busqueda} representa una busqueda de objetos persistentes en 
 * una base datos a través de hibernate.
 * @author kiira
 * @version 1.0, Nov 19, 2011
 */
public class Busqueda<K> {

    /** Criterios utilizados en la busqueda. */
    private DetachedCriteria criterios;

    /**
     * Inicializa la busqueda, creando la criteria correspondiente según la 
     * clase de la variable de tipo K.
     */
    protected Busqueda( Class<K> clase ) {
        criterios = DetachedCriteria.forClass( clase , "busq");

    }//end constructor

    /**
     * Agrega un criterio a la lista de criterios.
     * @param criterio criterio a agregar.
     */
    protected void agregarCriterio( Criterion criterio ) {
        criterios.add( criterio );

    }//end método agregarCriterio    
    
    /**
     * Permite crear un alias a alguna entidad asociada, para poder
     * referenciarla en futuros criterios.
     * @param associationPath path "punteado" de la asociación.
     * @param alias alias a asignar a la entidad.
     */
    protected void crearAlias( String associationPath, String alias) {
        criterios.createAlias( associationPath, alias );
        
    }//end método crearAlias

    /**
     * Permite indicar que propiedad se desea utilizar para ordenar
     * los resultados.
     * @param propiedad propiedad a utilizar para el ordenamiento.
     * @param desc permite indicar si se debe ordenar de forma 
     * ascendente o descendente:
     * <li>true - se ordena de forma descendente.
     * <li>false - se ordena de forma ascendente.
     */
    public void ordenarPor( String propiedad, boolean desc ) {
        //creamos y agregamos el orden
        Order orden = desc ? Order.desc( propiedad ) : Order.asc( propiedad );

        criterios.addOrder( orden );

    }//end método agregarOrden

    /**
     * Devuelve una lista con el resultado de la busqueda realizada
     * con los criterios aplicados.
     * @return lista de resultados de la busqueda.
     */
    public List<K> listarResultados() {
        //obtenemos una sesion e iniciamos la transaccion
        Session sesion = HibernateUtil.getCurrentSession();
        
        //lista para guardar los resultados
        List<K> resultados;
        
        synchronized ( sesion ) {
            //iniciamos la transacción
            sesion.beginTransaction();

            //creamos y ejecutamos la criteria real a partir de la sesion
            resultados = criterios.getExecutableCriteria( sesion ).list();

            //commit y cierre de sesion
            sesion.getTransaction().commit();

        }//end synchronized block

        return resultados;

    }//end método listarResultados

    /**
     * Devuelve un único resultado de la busqueda. Solo debería usarse
     * cuando se sabe que solo se debería encontrar un resultado, de no
     * ser así, el resto de los resultados se pierde.
     * @return 
     */
    public K resultadoUnico() {
        //obtenemos una sesion e iniciamos la transaccion
        Session sesion = HibernateUtil.getCurrentSession();
        
        //variable para guardar el resultado
        K resultado;
        
        synchronized ( sesion ) {
            //iniciamos la transaccion
            sesion.beginTransaction();

            //creamos y ejecutamos la criteria real a partir de la sesion
            resultado = (K)criterios.getExecutableCriteria( sesion ).uniqueResult();

            //commit y cierre de sesion
            sesion.getTransaction().commit();

        }//end synchronized block
        
        return resultado;

    }//end método resultadoUnico
    
    /**
     * Devuelve una criteria interna para realizar relaciones mas complejas
     * entre propiedades de la clase.
     * @param associationPath path "punteado" de la asociación entre entidades.
     * @return sub criteria de la criteria principal.
     */
    protected DetachedCriteria obtenerSubCriteria( String associationPath) {
        return criterios.createCriteria( associationPath );
        
    }//end método obtenerSubCriteria

}//end clase Busqueda

